home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / filter.c < prev    next >
C/C++ Source or Header  |  1996-03-17  |  65KB  |  2,848 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: filter.c,v 4.72 1996/03/18 18:48:55 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      filter.c
  44.  
  45.      This code provides a generalized, flexible way to allow
  46.      piping of data thru filters.  Each filter is passed a structure
  47.      that it will use to hold its static data while it operates on 
  48.      the stream of characters that are passed to it.  After processing
  49.      it will either return or call the next filter in 
  50.      the pipe with any character (or characters) it has ready to go. This
  51.      means some terminal type of filter has to be the last in the 
  52.      chain (i.e., one that writes the passed char someplace, but doesn't
  53.      call another filter).
  54.  
  55.      See below for more details.
  56.  
  57.      The motivation is to handle MIME decoding, richtext conversion, 
  58.      iso_code stripping and anything else that may come down the
  59.      pike (e.g., PEM) in an elegant fashion.  mikes (920811)
  60.  
  61.    TODO:
  62.        reasonable error handling
  63.  
  64.   ====*/
  65.  
  66.  
  67. #include "headers.h"
  68.  
  69.  
  70. /*
  71.  * Internal prototypes
  72.  */
  73. int    gf_so_readc PROTO((unsigned char *));
  74. int    gf_so_writec PROTO((int));
  75. int    gf_sreadc PROTO((unsigned char *));
  76. int    gf_swritec PROTO((int));
  77. int    gf_freadc PROTO((unsigned char *));
  78. int    gf_fwritec PROTO((int));
  79. void    gf_terminal PROTO((FILTER_S *, int));
  80. char   *gf_filter_puts PROTO((char *));
  81. void    gf_filter_eod PROTO((void));
  82. void    gf_error PROTO((char *));
  83. void    gf_8bit_put PROTO((FILTER_S *, int));
  84. int    so_reaquire PROTO((STORE_S *));
  85. int    so_cs_writec PROTO((int, STORE_S *));
  86. int    so_pico_writec PROTO((int, STORE_S *));
  87. int    so_file_writec PROTO((int, STORE_S *));
  88. int    so_cs_readc PROTO((unsigned char *, STORE_S *));
  89. int    so_pico_readc PROTO((unsigned char *, STORE_S *));
  90. int    so_file_readc PROTO((unsigned char *, STORE_S *));
  91. int    so_cs_puts PROTO((STORE_S *, char *));
  92. int    so_pico_puts PROTO((STORE_S *, char *));
  93. int    so_file_puts PROTO((STORE_S *, char *));
  94.  
  95.  
  96. /*
  97.  * GENERALIZED STORAGE FUNCTIONS.  Idea is to allow creation of
  98.  * storage objects that can be written into and read from without
  99.  * the caller knowing if the storage is core or in a file 
  100.  * or whatever.
  101.  */
  102. #define    MSIZE_INIT    8192
  103. #define    MSIZE_INC    4096
  104.  
  105. #ifdef    DOS
  106. #define    NO_PIPE
  107. #define    CRLF_NEWLINES
  108. #define    READ_MODE    "rb"
  109. #define    APPEND_MODE    "a+b"
  110. #else
  111. #ifdef OS2
  112. #define CRLF_NEWLINES
  113. #define READ_MODE    "rb"
  114. #define APPEND_MODE    "a+b"
  115. #else
  116. #define    READ_MODE    "r"
  117. #define    APPEND_MODE    "a+"
  118. #endif
  119. #endif
  120.  
  121.  
  122. /*
  123.  * allocate resources associated with the specified type of
  124.  * storage.  If requesting a named file object, open it for 
  125.  * appending, else just open a temp file.
  126.  *
  127.  * return the filled in storage object
  128.  */
  129. STORE_S *
  130. so_get(source, name, rtype)
  131.     SourceType  source;            /* requested storage type */
  132.     char       *name;            /* file name           */
  133.     int        rtype;            /* file access type      */
  134. {
  135.     STORE_S *so;
  136.     char    *type = (rtype&WRITE_ACCESS) ? APPEND_MODE : READ_MODE;
  137.  
  138.     so = (STORE_S *)fs_get(sizeof(STORE_S));
  139.     memset(so, 0, sizeof(STORE_S));
  140.     so->flags |= rtype;
  141.     
  142.     if(name)                    /* stash the name */
  143.       so->name = cpystr(name);
  144. #ifdef    DOS
  145.     else if(source == TmpFileStar || source == FileStar){
  146.     /*
  147.      * Coerce to TmpFileStar.  The MSC library's "tmpfile()"
  148.      * doesn't observe the "TMP" or "TEMP" environment vars and 
  149.      * always wants to write "\".  This is problematic in shared,
  150.      * networked environments.
  151.      */
  152.     source   = TmpFileStar;
  153.     so->name = temp_nam(NULL, "pi");
  154.     }
  155. #else
  156.     else if(source == TmpFileStar)        /* make one up! */
  157.       so->name = temp_nam(NULL, "pine-tmp");
  158. #endif
  159.  
  160.     so->src = source;
  161.     if(so->src == FileStar || so->src == TmpFileStar){
  162.     so->writec = so_file_writec;
  163.     so->readc  = so_file_readc;
  164.     so->puts   = so_file_puts;
  165.  
  166.     /*
  167.      * The reason for both FileStar and TmpFileStar types is
  168.      * that, named or unnamed, TmpFileStar's are unlinked
  169.      * when the object is given back to the system.  This is
  170.      * useful for keeping us from running out of file pointers as
  171.      * the pointer associated with the object can be temporarily
  172.      * returned to the system without destroying the object.
  173.      * 
  174.      * The programmer is warned to be careful not to assign the
  175.      * TmpFileStar type to any files that are expected to remain
  176.      * after the dust has settled!
  177.      */
  178.     if(so->name){
  179.         if(!(so->txt = (void *)fopen(so->name, type))){
  180.         dprint(1, (debugfile, "so_get error: %s : %s", so->name,
  181.                error_description(errno)));
  182.         fs_give((void **)&so->name);
  183.         fs_give((void **)&so);         /* so freed & set to NULL */
  184.         }
  185.     }
  186.     else{
  187.         if(!(so->txt = (void *)create_tmpfile())){
  188.         dprint(1, (debugfile, "so_get error: tmpfile : %s",
  189.                error_description(errno)));
  190.         fs_give((void **)&so);        /* so freed & set to NULL */
  191.         }
  192.     }
  193.     }
  194.     else if(so->src == PicoText){
  195.     so->writec = so_pico_writec;
  196.     so->readc  = so_pico_readc;
  197.     so->puts   = so_pico_puts;
  198.     if(!(so->txt = pico_get())){
  199.         dprint(1, (debugfile, "so_get error: alloc of pico text space"));
  200.         if(so->name)
  201.           fs_give((void **)&so->name);
  202.         fs_give((void **)&so);        /* so freed & set to NULL */
  203.     }
  204.     }
  205.     else{
  206.     so->writec = so_cs_writec;
  207.     so->readc  = so_cs_readc;
  208.     so->puts   = so_cs_puts;
  209.     so->txt       = (void *)fs_get((size_t) MSIZE_INIT * sizeof(char));
  210.     so->dp       = so->eod = (unsigned char *) so->txt;
  211.     so->eot       = so->dp + MSIZE_INIT;
  212.     memset(so->eod, 0, so->eot - so->eod);
  213.     }
  214.  
  215.     return(so);
  216. }
  217.  
  218.  
  219. /* 
  220.  * so_give - free resources associated with a storage object and then
  221.  *           the object itself.
  222.  */
  223. void
  224. so_give(so)
  225. STORE_S **so;
  226. {
  227.     if(!so)
  228.       return;
  229.  
  230.     if((*so)->src == FileStar || (*so)->src == TmpFileStar){
  231.         if((*so)->txt)
  232.       fclose((FILE *)(*so)->txt);    /* disassociate from storage */
  233.  
  234.     if((*so)->name && (*so)->src == TmpFileStar)
  235.       unlink((*so)->name);        /* really disassociate! */
  236.     }
  237.     else if((*so)->txt && (*so)->src == PicoText)
  238.       pico_give((*so)->txt);
  239.     else if((*so)->txt)
  240.       fs_give((void **)&((*so)->txt));
  241.  
  242.     if((*so)->name)
  243.       fs_give((void **)&((*so)->name));    /* blast the name            */
  244.  
  245.     fs_give((void **)so);        /* release the object        */
  246. }
  247.  
  248.  
  249. /*
  250.  * put a character into the specified storage object, 
  251.  * expanding if neccessary 
  252.  *
  253.  * return 1 on success and 0 on failure
  254.  */
  255. int
  256. so_cs_writec(c, so)
  257.     int      c;
  258.     STORE_S *so;
  259. {
  260.     register unsigned char  ch = (unsigned char) c;
  261.  
  262.     if(so->dp >= so->eot){
  263.     register size_t cur_o  = so->dp - (unsigned char *)so->txt;
  264.     register size_t data_o = so->eod - (unsigned char *)so->txt;
  265.     register size_t size   = (so->eot-(unsigned char *)so->txt)+MSIZE_INC;
  266.     fs_resize(&so->txt, size * sizeof(char));
  267.     so->dp   = (unsigned char *) so->txt + cur_o;
  268.     so->eod  = (unsigned char *) so->txt + data_o;
  269.     so->eot  = (unsigned char *) so->txt + size;
  270.     memset(so->eod, 0, so->eot - so->eod);
  271.     }
  272.  
  273.     *so->dp++ = ch;
  274.     if(so->dp > so->eod)
  275.       so->eod = so->dp;
  276.  
  277.     return(1);
  278. }
  279.  
  280. int
  281. so_pico_writec(c, so)
  282.     int      c;
  283.     STORE_S *so;
  284. {
  285.     unsigned char ch = (unsigned char) c;
  286.  
  287.     return(pico_writec(so->txt, ch));
  288. }
  289.  
  290. int
  291. so_file_writec(c, so)
  292.     int      c;
  293.     STORE_S *so;
  294. {
  295.     unsigned char ch = (unsigned char) c;
  296.     int rv = 0;
  297.  
  298.     if(so->txt || so_reaquire(so))
  299.       do
  300.     rv = fwrite(&ch,sizeof(unsigned char),(size_t)1,(FILE *)so->txt);
  301.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  302.  
  303.     return(rv);
  304. }
  305.  
  306.  
  307. /*
  308.  * get a character from the specified storage object.
  309.  * 
  310.  * return 1 on success and 0 on failure
  311.  */
  312. int
  313. so_cs_readc(c, so)
  314.     unsigned char *c;
  315.     STORE_S       *so;
  316. {
  317.     return((so->dp < so->eod) ? *c = *(so->dp)++, 1 : 0);
  318. }
  319.  
  320. int
  321. so_pico_readc(c, so)
  322.     unsigned char *c;
  323.     STORE_S       *so;
  324. {
  325.     return(pico_readc(so->txt, c));
  326. }
  327.  
  328. int
  329. so_file_readc(c, so)
  330.     unsigned char *c;
  331.     STORE_S       *so;
  332. {
  333.     int rv = 0;
  334.  
  335.     if(so->txt || so_reaquire(so))
  336.       do
  337.     rv = fread(c, sizeof(char), (size_t)1, (FILE *)so->txt);
  338.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  339.  
  340.     return(rv);
  341. }
  342.  
  343.  
  344. /* 
  345.  * write a string into the specified storage object, 
  346.  * expanding if necessary (and cheating if the object 
  347.  * happens to be a file!)
  348.  *
  349.  * return 1 on success and 0 on failure
  350.  */
  351. int
  352. so_cs_puts(so, s)
  353.     STORE_S *so;
  354.     char    *s;
  355. {
  356.     int slen = strlen(s);
  357.  
  358.     if(so->dp + slen >= so->eot){
  359.     register size_t cur_o  = so->dp - (unsigned char *) so->txt;
  360.     register size_t data_o = so->eod - (unsigned char *) so->txt;
  361.     register size_t len   = so->eot - (unsigned char *) so->txt;
  362.     while(len <= cur_o + slen + 1)
  363.       len += MSIZE_INC;        /* need to resize! */
  364.  
  365.     fs_resize(&so->txt, len * sizeof(char));
  366.     so->dp     = (unsigned char *)so->txt + cur_o;
  367.     so->eod     = (unsigned char *)so->txt + data_o;
  368.     so->eot     = (unsigned char *)so->txt + len;
  369.     memset(so->eod, 0, so->eot - so->eod);
  370.     }
  371.  
  372.     memcpy(so->dp, s, slen);
  373.     so->dp += slen;
  374.     if(so->dp > so->eod)
  375.       so->eod = so->dp;
  376.  
  377.     return(1);
  378. }
  379.  
  380. int
  381. so_pico_puts(so, s)
  382.     STORE_S *so;
  383.     char    *s;
  384. {
  385.     return(pico_puts(so->txt, s));
  386. }
  387.  
  388. int
  389. so_file_puts(so, s)
  390.     STORE_S *so;
  391.     char    *s;
  392. {
  393.     int rv = *s ? 0 : 1;
  394.  
  395.     if(!rv && (so->txt || so_reaquire(so)))
  396.       do
  397.     rv = fwrite(s, strlen(s)*sizeof(char), (size_t)1, (FILE *)so->txt);
  398.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  399.  
  400.     return(rv);
  401. }
  402.  
  403.  
  404.  
  405. /*
  406.  * Position the storage object's pointer to the given offset
  407.  * from the start of the object's data.
  408.  */
  409. int
  410. so_seek(so, pos, orig)
  411.     STORE_S *so;
  412.     long     pos;
  413.     int      orig;
  414. {
  415.     if(so->src == CharStar){
  416.     switch(orig){
  417.         case 0 :                /* SEEK_SET */
  418.           return((pos < so->eod - (unsigned char *) so->txt)
  419.               ? so->dp = (unsigned char *)so->txt + pos, 0 : -1);
  420.         case 1 :                /* SEEK_CUR */
  421.           return((pos > 0)
  422.                ? ((pos < so->eod - so->dp) ? so->dp += pos, 0: -1)
  423.                : ((pos < 0)
  424.                ? ((-pos < so->dp - (unsigned char *)so->txt)
  425.                     ? so->dp += pos, 0 : -1)
  426.                : 0));
  427.         case 2 :                /* SEEK_END */
  428.           return((pos < so->eod - (unsigned char *) so->txt)
  429.               ? so->dp = so->eod - pos, 0 : -1);
  430.         default :
  431.           return(-1);
  432.     }
  433.     }
  434.     else if(so->src == PicoText)
  435.       return(pico_seek(so->txt, pos, orig));
  436.     else            /* FileStar or TmpFileStar */
  437.       return((so->txt || so_reaquire(so)) && fseek((FILE *)so->txt,pos,orig));
  438. }
  439.  
  440.  
  441. /*
  442.  * Change the given storage object's size to that specified.  If size
  443.  * is less than the current size, the internal pointer is adjusted and
  444.  * all previous data beyond the given size is lost.
  445.  */
  446. int
  447. so_truncate(so, size)
  448.     STORE_S *so;
  449.     long     size;
  450. {
  451.     if(so->src == CharStar){
  452.     if(so->eod < (unsigned char *) so->txt + size){    /* alloc! */
  453.         unsigned char *newtxt = (unsigned char *) so->txt;
  454.         register size_t len   = so->eot - (unsigned char *) so->txt;
  455.  
  456.         while(len <= size)
  457.           len += MSIZE_INC;        /* need to resize! */
  458.  
  459.         if(len > so->eot - (unsigned char *) newtxt){
  460.         fs_resize((void *) &newtxt, len * sizeof(char));
  461.         so->eot = newtxt + len;
  462.         so->eod = newtxt + (so->eod - (unsigned char *) so->txt);
  463.         memset(so->eod, 0, so->eot - so->eod);
  464.         }
  465.  
  466.         so->eod = newtxt + size;
  467.         so->dp  = newtxt + (so->dp - (unsigned char *) so->txt);
  468.         so->txt = newtxt;
  469.     }
  470.     else if(so->eod > (unsigned char *) so->txt + size){
  471.         if(so->dp > (so->eod = (unsigned char *)so->txt + size))
  472.           so->dp = so->eod;
  473.  
  474.         memset(so->eod, 0, so->eot - so->eod);
  475.     }
  476.     }
  477.     else if(so->src == PicoText)
  478.       fatal("programmer botch: unsupported so_truncate call");
  479.     else            /* FileStar or TmpFileStar */
  480.       return(ftruncate(fileno((FILE *)so->txt), size));
  481. }
  482.  
  483.  
  484. /*
  485.  * so_release - a rather misnamed function.  the idea is to release
  486.  *              what system resources we can (e.g., open files).
  487.  *              while maintaining a reference to it.
  488.  *              it's up to the functions that deal with this object
  489.  *              next to re-aquire those resources.
  490.  */
  491. int
  492. so_release(so)
  493. STORE_S *so;
  494. {
  495.     if(so->txt && so->name && (so->src == FileStar || so->src == TmpFileStar)){
  496.     if(fget_pos((FILE *)so->txt, (fpos_t *)&(so->used)) == 0){
  497.         fclose((FILE *)so->txt);        /* free the handle! */
  498.         so->txt = NULL;
  499.     }
  500.     }
  501.  
  502.     return(1);
  503. }
  504.  
  505.  
  506. /*
  507.  * so_reaquire - get any previously released system resources we
  508.  *               may need for the given storage object.
  509.  *       NOTE: at the moment, only FILE * types of objects are
  510.  *             effected, so it only needs to be called before
  511.  *             references to them.
  512.  *                     
  513.  */
  514. so_reaquire(so)
  515. STORE_S *so;
  516. {
  517.     int   rv = 1;
  518.     char  *type = ((so->flags)&WRITE_ACCESS) ? APPEND_MODE : READ_MODE;
  519.  
  520.     if(!so->txt && (so->src == FileStar || so->src == TmpFileStar)){
  521.     if(!(so->txt=(void *)fopen(so->name, type))){
  522.         q_status_message2(SM_ORDER,3,5, "ERROR reopening %s : %s", so->name,
  523.                 error_description(errno));
  524.         rv = 0;
  525.     }
  526.     else if(fset_pos((FILE *)so->txt, (fpos_t *)&(so->used))){
  527.         q_status_message2(SM_ORDER, 3, 5, "ERROR positioning in %s : %s", 
  528.                 so->name, error_description(errno));
  529.         rv = 0;
  530.     }
  531.     }
  532.  
  533.     return(rv);
  534. }
  535.  
  536.  
  537. /*
  538.  * so_text - return a pointer to the text the store object passed
  539.  */
  540. void *
  541. so_text(so)
  542. STORE_S *so;
  543. {
  544.     return((so) ? so->txt : NULL);
  545. }
  546.  
  547.  
  548. /*
  549.  * END OF GENERALIZE STORAGE FUNCTIONS
  550.  */
  551.  
  552.  
  553. /*
  554.  * Start of filters, pipes and various support functions
  555.  */
  556.  
  557. /*
  558.  * pointer to first function in a pipe, and pointer to last filter
  559.  */
  560. FILTER_S         *gf_master = NULL;
  561. static    gf_io_t   last_filter;
  562. static    char     *gf_error_string;
  563. static    long      gf_byte_count;
  564. static    jmp_buf   gf_error_state;
  565.  
  566.  
  567. /*
  568.  * A list of states used by the various filters.  Reused in many filters.
  569.  */
  570. #define    DFL    0
  571. #define    EQUAL    1
  572. #define    HEX    2
  573. #define    WSPACE    3
  574. #define    CCR    4
  575. #define    CLF    5
  576. #define    CESCP    6
  577. #define    TOKEN    7
  578. #define    LITERAL    8
  579.  
  580.  
  581. /*
  582.  * Macros to reduce function call overhead associated with calling
  583.  * each filter for each byte filtered, and to minimize filter structure
  584.  * dereferences.  NOTE: "queuein" has to do with putting chars into the
  585.  * filter structs data queue.  So, writing at the queuein offset is 
  586.  * what a filter does to pass processed data out of itself.  Ditto for
  587.  * queueout.  This explains the FI --> queueout init stuff below.
  588.  */
  589. #define    GF_QUE_START(F)    (&(F)->queue[0])
  590. #define    GF_QUE_END(F)    (&(F)->queue[GF_MAXBUF - 1])
  591.  
  592. #define    GF_IP_INIT(F)    ip  = (F) ? &(F)->queue[(F)->queuein] : NULL
  593. #define    GF_EIB_INIT(F)    eib = (F) ? GF_QUE_END(F) : NULL
  594. #define    GF_OP_INIT(F)    op  = (F) ? &(F)->queue[(F)->queueout] : NULL
  595. #define    GF_EOB_INIT(F)    eob = (F) ? &(F)->queue[(F)->queuein] : NULL
  596.  
  597. #define    GF_IP_END(F)    (F)->queuein  = ip - GF_QUE_START(F)
  598. #define    GF_OP_END(F)    (F)->queueout = op - GF_QUE_START(F)
  599.  
  600. #define    GF_INIT(FI, FO)    register unsigned char *GF_OP_INIT(FI);     \
  601.             register unsigned char *GF_EOB_INIT(FI); \
  602.             register unsigned char *GF_IP_INIT(FO);  \
  603.             register unsigned char *GF_EIB_INIT(FO);
  604.  
  605. #define    GF_CH_RESET(F)    ((int)(op = eob = GF_QUE_START(F), \
  606.                         (F)->queueout = (F)->queuein = 0))
  607.  
  608. #define    GF_END(FI, FO)    (GF_OP_END(FI), GF_IP_END(FO))
  609.  
  610. #define    GF_FLUSH(F)    ((int)(GF_IP_END(F), (*(F)->f)((F), GF_DATA), \
  611.                    GF_IP_INIT(F), GF_EIB_INIT(F)))
  612.  
  613. #define    GF_PUTC(F, C)    ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1))
  614.  
  615. #define    GF_GETC(F, C)    ((op < eob) ? ((int)((C) = *op++), 1) : GF_CH_RESET(F))
  616.  
  617.  
  618. /*
  619.  * Generalized getc and putc routines.  provided here so they don't
  620.  * need to be re-done elsewhere to 
  621.  */
  622.  
  623. /*
  624.  * pointers to objects to be used by the generic getc and putc
  625.  * functions
  626.  */
  627. static struct gf_io_struct {
  628.     FILE          *file;
  629.     char          *txtp;
  630.     unsigned long  n;
  631. } gf_in, gf_out;
  632. static STORE_S *gf_so_in, *gf_so_out;
  633.  
  634.  
  635. /*
  636.  * setup to use and return a pointer to the generic
  637.  * getc function
  638.  */
  639. void
  640. gf_set_readc(gc, txt, len, src)
  641.     gf_io_t       *gc;
  642.     void          *txt;
  643.     unsigned long  len;
  644.     SourceType     src;
  645. {
  646.     gf_in.n = len;
  647.     if(src == FileStar){
  648.     gf_in.file = (FILE *)txt;
  649.     fseek(gf_in.file, 0L, 0);
  650.     *gc = gf_freadc;
  651.     }
  652.     else{
  653.     gf_in.txtp = (char *)txt;
  654.     *gc = gf_sreadc;
  655.     }
  656. }
  657.  
  658.  
  659. /*
  660.  * setup to use and return a pointer to the generic
  661.  * putc function
  662.  */
  663. void
  664. gf_set_writec(pc, txt, len, src)
  665.     gf_io_t       *pc;
  666.     void          *txt;
  667.     unsigned long  len;
  668.     SourceType     src;
  669. {
  670.     gf_out.n = len;
  671.     if(src == FileStar){
  672.     gf_out.file = (FILE *)txt;
  673.     *pc = gf_fwritec;
  674.     }
  675.     else{
  676.     gf_out.txtp = (char *)txt;
  677.     *pc = gf_swritec;
  678.     }
  679. }
  680.  
  681.  
  682. /*
  683.  * setup to use and return a pointer to the generic
  684.  * getc function
  685.  */
  686. void
  687. gf_set_so_readc(gc, so)
  688.     gf_io_t *gc;
  689.     STORE_S *so;
  690. {
  691.     gf_so_in = so;
  692.     *gc      = gf_so_readc;
  693. }
  694.  
  695.  
  696. /*
  697.  * setup to use and return a pointer to the generic
  698.  * putc function
  699.  */
  700. void
  701. gf_set_so_writec(pc, so)
  702.     gf_io_t *pc;
  703.     STORE_S *so;
  704. {
  705.     gf_so_out = so;
  706.     *pc       = gf_so_writec;
  707. }
  708.  
  709.  
  710. /*
  711.  * put the character to the object previously defined
  712.  */
  713. int
  714. gf_so_writec(c)
  715. int c;
  716. {
  717.     return(so_writec(c, gf_so_out));
  718. }
  719.  
  720.  
  721. /*
  722.  * get a character from an object previously defined
  723.  */
  724. int
  725. gf_so_readc(c)
  726. unsigned char *c;
  727. {
  728.     return(so_readc(c, gf_so_in));
  729. }
  730.  
  731.  
  732. /* get a character from a file */
  733. /* assumes gf_out struct is filled in */
  734. int
  735. gf_freadc(c)
  736. unsigned char *c;
  737. {
  738.     int rv = 0;
  739.  
  740.     do
  741.       rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file);
  742.     while(!rv && ferror(gf_in.file) && errno == EINTR);
  743.  
  744.     return(rv);
  745. }
  746.  
  747.  
  748. /* put a character to a file */
  749. /* assumes gf_out struct is filled in */
  750. int
  751. gf_fwritec(c)
  752.     int c;
  753. {
  754.     unsigned char ch = (unsigned char)c;
  755.     int rv = 0;
  756.  
  757.     do
  758.       rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file);
  759.     while(!rv && ferror(gf_out.file) && errno == EINTR);
  760.  
  761.     return(rv);
  762. }
  763.  
  764.  
  765. /* get a character from a string, return nonzero if things OK */
  766. /* assumes gf_out struct is filled in */
  767. int
  768. gf_sreadc(c)
  769. unsigned char *c;
  770. {
  771.     return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0);
  772. }
  773.  
  774.  
  775. /* put a character into a string, return nonzero if things OK */
  776. /* assumes gf_out struct is filled in */
  777. int
  778. gf_swritec(c)
  779.     int c;
  780. {
  781.     return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0);
  782. }
  783.  
  784.  
  785. /*
  786.  * output the given string with the given function
  787.  */
  788. int
  789. gf_puts(s, pc)
  790.     register char *s;
  791.     gf_io_t        pc;
  792. {
  793.     while(*s != '\0')
  794.       if(!(*pc)(*s++))
  795.     return(0);        /* ERROR putting char ! */
  796.  
  797.     return(1);
  798. }
  799.  
  800.  
  801. /*
  802.  * Start of generalized filter routines
  803.  */
  804.  
  805. /* 
  806.  * initializing function to make sure list of filters is empty.
  807.  */
  808. void
  809. gf_filter_init()
  810. {
  811.     FILTER_S *flt, *fltn = gf_master;
  812.  
  813.     while((flt = fltn) != NULL){    /* free list of old filters */
  814.     fltn = flt->next;
  815.     fs_give((void **)&flt);
  816.     }
  817.  
  818.     gf_master = NULL;
  819.     gf_error_string = NULL;        /* clear previous errors */
  820.     gf_byte_count = 0L;            /* reset counter */
  821. }
  822.  
  823.  
  824.  
  825. /*
  826.  * link the given filter into the filter chain
  827.  */
  828. gf_link_filter(f)
  829.     filter_t f;
  830. {
  831.     FILTER_S *new, *tail;
  832.  
  833.     new = (FILTER_S *)fs_get(sizeof(FILTER_S));
  834.     memset(new, 0, sizeof(FILTER_S));
  835.  
  836.     new->f = f;                /* set the function pointer     */
  837.     (*f)(new, GF_RESET);        /* have it setup initial state  */
  838.  
  839.     if(tail = gf_master){        /* or add it to end of existing  */
  840.     while(tail->next)        /* list  */
  841.       tail = tail->next;
  842.  
  843.     tail->next = new;
  844.     }
  845.     else                /* attach new struct to list    */
  846.       gf_master = new;            /* start a new list */
  847. }
  848.  
  849.  
  850. /*
  851.  * terminal filter, doesn't call any other filters, typically just does
  852.  * something with the output
  853.  */
  854. void
  855. gf_terminal(f, flg)
  856.     FILTER_S *f;
  857.     int       flg;
  858. {
  859.     if(flg == GF_DATA){
  860.     GF_INIT(f, f);
  861.  
  862.     while(op < eob)
  863.       if((*last_filter)(*op++) <= 0) /* generic terminal filter */
  864.         gf_error("Error writing out pipe");
  865.  
  866.     GF_CH_RESET(f);
  867.     }
  868. }
  869.  
  870.  
  871. /*
  872.  * set some outside gf_io_t function to the terminal function 
  873.  * for example: a function to write a char to a file or into a buffer
  874.  */
  875. void
  876. gf_set_terminal(f)            /* function to set generic filter */
  877.     gf_io_t f;
  878. {
  879.     last_filter = f;
  880. }
  881.  
  882.  
  883. /*
  884.  * common function for filter's to make it known that an error
  885.  * has occurred.  Jumps back to gf_pipe with error message.
  886.  */
  887. void
  888. gf_error(s)
  889.     char *s;
  890. {
  891.     /* let the user know the error passed in s */
  892.     gf_error_string = s;
  893.     longjmp(gf_error_state, 1);
  894. }
  895.  
  896.  
  897. /*
  898.  * The routine that shoves each byte through the chain of
  899.  * filters.  It sets up error handling, and the terminal function.
  900.  * Then loops getting bytes with the given function, and passing
  901.  * it on to the first filter in the chain.
  902.  */
  903. char *
  904. gf_pipe(gc, pc)
  905.     gf_io_t gc, pc;            /* how to get a character */
  906. {
  907.     unsigned char c;
  908.  
  909. #ifdef    DOS
  910.     MoveCursor(0, 1);
  911.     StartInverse();
  912. #endif
  913.  
  914.     dprint(4, (debugfile, "-- gf_pipe: "));
  915.  
  916.     /*
  917.      * set up for any errors a filter may encounter
  918.      */
  919.     if(setjmp(gf_error_state)){
  920. #ifdef    DOS
  921.     ibmputc(' ');
  922.     EndInverse();
  923. #endif
  924.     dprint(4, (debugfile, "ERROR: %s\n",
  925.            gf_error_string ? gf_error_string : "NULL"));
  926.     return(gf_error_string);     /*  */
  927.     }
  928.  
  929.     /*
  930.      * set and link in the terminal filter
  931.      */
  932.     gf_set_terminal(pc);
  933.     gf_link_filter(gf_terminal);
  934.  
  935.     /* 
  936.      * while there are chars to process, send them thru the pipe.
  937.      * NOTE: it's necessary to enclose the loop below in a block
  938.      * as the GF_INIT macro calls some automatic var's into
  939.      * existence.  It can't be placed at the start of gf_pipe
  940.      * because its useful for us to be called without filters loaded
  941.      * when we're just being used to copy bytes between storage
  942.      * objects.
  943.      */
  944.     {
  945.     GF_INIT(gf_master, gf_master);
  946.  
  947.     while((*gc)(&c)){
  948.         gf_byte_count++;
  949. #ifdef    DOS
  950.         if(!(gf_byte_count & 0x3ff))
  951. #ifdef    _WINDOWS
  952.           /* Under windows we yeild to allow event processing.
  953.            * Progress display is handled throught the alarm()
  954.            * mechinism.
  955.            */
  956.           mswin_yeild ();
  957. #else
  958.           /* Poor PC still needs spinning bar */
  959.           ibmputc("/-\\|"[((int) gf_byte_count >> 10) % 4]);
  960.           MoveCursor(0, 1);
  961. #endif
  962. #endif
  963.  
  964.         GF_PUTC(gf_master, c & 0xff);
  965.     }
  966.  
  967.     /*
  968.      * toss an end-of-data marker down the pipe to give filters
  969.      * that have any buffered data the opportunity to dump it
  970.      */
  971.     GF_FLUSH(gf_master);
  972.     (*gf_master->f)(gf_master, GF_EOD);
  973.     }
  974.  
  975. #ifdef    DOS
  976.     ibmputc(' ');
  977.     EndInverse();
  978. #endif
  979.  
  980.     dprint(1, (debugfile, "done.\n"));
  981.     return(NULL);            /* everything went OK */
  982. }
  983.  
  984.  
  985. /*
  986.  * return the number of bytes piped so far
  987.  */
  988. long
  989. gf_bytes_piped()
  990. {
  991.     return(gf_byte_count);
  992. }
  993.  
  994.  
  995. /*
  996.  * filter the given input with the given command
  997.  *
  998.  *  Args: cmd -- command string to execute
  999.  *    prepend -- string to prepend to filtered input
  1000.  *    source_so -- storage object containing data to be filtered
  1001.  *    pc -- function to write filtered output with
  1002.  *    aux_filters -- additional filters to pass data thru after "cmd"
  1003.  *
  1004.  *  Returns: NULL on sucess, reason for failure (not alloc'd!) on error
  1005.  */
  1006. char *
  1007. gf_filter(cmd, prepend, source_so, pc, aux_filters)
  1008.     char     *cmd, *prepend;
  1009.     STORE_S  *source_so;
  1010.     gf_io_t   pc;
  1011.     filter_t *aux_filters;
  1012. {
  1013.     unsigned char c;
  1014.     int         flags;
  1015.     char   *errstr = NULL, buf[MAILTMPLEN], *rfile = NULL;
  1016.     PIPE_S *fpipe;
  1017.  
  1018.     gf_filter_init();
  1019.     while(aux_filters && *aux_filters)
  1020.       gf_link_filter(*aux_filters++);
  1021.  
  1022.     gf_set_terminal(pc);
  1023.     gf_link_filter(gf_terminal);
  1024.  
  1025.     /*
  1026.      * Spawn filter feeding it data, and reading what it writes.
  1027.      */
  1028.     so_seek(source_so, 0L, 0);
  1029. #ifdef    NO_PIPE
  1030.     /*
  1031.      * When there're no pipes for IPC, use an output file to collect
  1032.      * the result...
  1033.      */
  1034.     flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_RESET;
  1035.     rfile = temp_nam(NULL, "pf");
  1036. #else
  1037.     flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL | PIPE_RESET;
  1038. #endif
  1039.  
  1040.     if(fpipe = open_system_pipe(cmd, rfile ? &rfile : NULL, NULL, flags)){
  1041. #ifdef    NO_PIPE
  1042.     if(prepend && (fputs(prepend, fpipe->ofilep) == EOF
  1043.                || fputc('\n', fpipe->ofilep) == EOF))
  1044.       errstr = error_description(errno);
  1045.  
  1046.     /*
  1047.      * Write the output, and deal with the result later...
  1048.      */
  1049.     while(!errstr && so_readc(&c, source_so))
  1050.       if(fputc(c, fpipe->ofilep) == EOF)
  1051.         errstr = error_description(errno);
  1052. #else
  1053. #ifdef    NON_BLOCKING_IO
  1054.     int     n;
  1055.  
  1056.     if(fcntl(fileno(fpipe->ifilep), F_SETFL, NON_BLOCKING_IO) == -1)
  1057.       errstr = "Can't set up non-blocking IO";
  1058.  
  1059.     if(prepend && (fputs(prepend, fpipe->ofilep) == EOF
  1060.                || fputc('\n', fpipe->ofilep) == EOF))
  1061.       errstr = error_description(errno);
  1062.  
  1063.     while(!errstr){
  1064.         /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX
  1065.          * isn't ubiquitous ;).
  1066.          */
  1067.         for(n = 0; !errstr && fpipe->ofilep && n < 1024; n++)
  1068.           if(!so_readc(&c, source_so)){
  1069.           fclose(fpipe->ofilep);
  1070.           fpipe->ofilep = NULL;
  1071.           }
  1072.           else if(fputc(c, fpipe->ofilep) == EOF)
  1073.         errstr = error_description(errno);
  1074.  
  1075.         /*
  1076.          * Note: We clear errno here and test below, before ferror,
  1077.          *         because *some* stdio implementations consider
  1078.          *         EAGAIN and EWOULDBLOCK equivalent to EOF...
  1079.          */
  1080.         errno = 0;
  1081.  
  1082.         while(!errstr && fgets(buf, MAILTMPLEN, fpipe->ifilep))
  1083.           errstr = gf_filter_puts(buf);
  1084.  
  1085.         /* then fgets failed! */
  1086.         if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){
  1087.         if(feof(fpipe->ifilep))        /* nothing else interesting! */
  1088.           break;            
  1089.         else if(ferror(fpipe->ifilep))    /* bummer. */
  1090.           errstr = error_description(errno);
  1091.         }
  1092.     }
  1093. #else
  1094.     if(prepend && (fputs(prepend, fpipe->ofilep) == EOF
  1095.                || fputc('\n', fpipe->ofilep) == EOF))
  1096.       errstr = error_description(errno);
  1097.  
  1098.     /*
  1099.      * Well, do the best we can, and hope the pipe we're writing
  1100.      * doesn't fill up before we start reading...
  1101.      */
  1102.     while(!errstr && so_readc(&c, source_so))
  1103.       if(fputc(c, fpipe->ofilep) == EOF)
  1104.         errstr = error_description(errno);
  1105.  
  1106.     fclose(fpipe->ofilep);
  1107.     fpipe->ofilep = NULL;
  1108.     while(!errstr && fgets(buf, MAILTMPLEN, fpipe->ifilep))
  1109.       errstr = gf_filter_puts(buf);
  1110. #endif /* NON_BLOCKING */
  1111. #endif /* NO_PIPE */
  1112.  
  1113.     gf_filter_eod();
  1114.  
  1115.     if(close_system_pipe(&fpipe) && !errstr)
  1116.       errstr = "Pipe command returned error.";
  1117.  
  1118. #ifdef    NO_PIPE
  1119.     /*
  1120.      * retrieve filters result...
  1121.      */
  1122.     {
  1123.         FILE *fp;
  1124.         if(fp = fopen(rfile, "rb"))
  1125.           while(!errstr && fgets(buf, MAILTMPLEN, fp))
  1126.         errstr = gf_filter_puts(buf);
  1127.  
  1128.         fs_give((void **)&rfile);
  1129.     }
  1130. #endif
  1131.     }
  1132.  
  1133.     return(errstr);
  1134. }
  1135.  
  1136.  
  1137. /*
  1138.  * gf_filter_puts - write the given string down the filter's pipe
  1139.  */
  1140. char *
  1141. gf_filter_puts(s)
  1142.     register char *s;
  1143. {
  1144.     GF_INIT(gf_master, gf_master);
  1145.  
  1146.     /*
  1147.      * set up for any errors a filter may encounter
  1148.      */
  1149.     if(setjmp(gf_error_state)){
  1150.     dprint(4, (debugfile, "ERROR: gf_filter_puts: %s\n",
  1151.            gf_error_string ? gf_error_string : "NULL"));
  1152.     return(gf_error_string);
  1153.     }
  1154.  
  1155.     while(*s)
  1156.       GF_PUTC(gf_master, (*s++) & 0xff);
  1157.  
  1158.     GF_END(gf_master, gf_master);
  1159.     return(NULL);
  1160. }
  1161.  
  1162.  
  1163. /*
  1164.  * gf_filter_eod - flush pending data filter's input queue and deliver
  1165.  *           the GF_EOD marker.
  1166.  */
  1167. void
  1168. gf_filter_eod()
  1169. {
  1170.     GF_INIT(gf_master, gf_master);
  1171.     GF_FLUSH(gf_master);
  1172.     (*gf_master->f)(gf_master, GF_EOD);
  1173. }
  1174.  
  1175.  
  1176.  
  1177.  
  1178. /*
  1179.  * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS
  1180.  *
  1181.  * Filters MUST use the specified interface (pointer to filter
  1182.  * structure, the unsigned character buffer in that struct, and a
  1183.  * cmd flag), and pass each resulting octet to the next filter in the
  1184.  * chain.  Only the terminal filter need not call another filter.
  1185.  * As a result, filters share a pretty general structure.
  1186.  * Typically three main conditionals separate initialization from
  1187.  * data from end-of-data command processing.
  1188.  * 
  1189.  * Lastly, being character-at-a-time, they're a little more complex
  1190.  * to write than filters operating on buffers because some state
  1191.  * must typically be kept between characters.  However, for a
  1192.  * little bit of complexity here, much convenience is gained later
  1193.  * as they can be arbitrarily chained together at run time and
  1194.  * consume few resources (especially memory or disk) as they work.
  1195.  * (NOTE 951005: even less cpu now that data between filters is passed
  1196.  *  via a vector.)
  1197.  *
  1198.  * A few notes about implementing filters:
  1199.  *
  1200.  *  - A generic filter template looks like:
  1201.  *
  1202.  *    void
  1203.  *    gf_xxx_filter(f, flg)
  1204.  *        FILTER_S *f;
  1205.  *        int       flg;
  1206.  *    {
  1207.  *      GF_INIT(f, f->next);        // def's var's to speed queue drain
  1208.  *
  1209.  *        if(flg == GF_DATA){
  1210.  *          register unsigned char c;
  1211.  *
  1212.  *          while(GF_GETC(f, c)){    // macro taking data off input queue
  1213.  *              // operate on c and pass it on here
  1214.  *                GF_PUTC(f->next, c);    // macro writing output queue
  1215.  *          }
  1216.  *
  1217.  *          GF_END(f, f->next);    // macro to sync pointers/offsets
  1218.  *          //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED
  1219.  *        }
  1220.  *        else if(flg == GF_EOD){
  1221.  *            // process any buffered data here and pass it on
  1222.  *          GF_FLUSH(f->next);    // flush pending data to next filter
  1223.  *            (*f->next->f)(f->next, GF_EOD);
  1224.  *        }
  1225.  *        else if(flg == GF_RESET){
  1226.  *            // initialize any data in the struct here
  1227.  *        }
  1228.  *    }
  1229.  *
  1230.  *  - Any free storage allocated during initialization (typically tied
  1231.  *    to the "line" pointer in FILTER_S) is the filter's responsibility
  1232.  *    to clean up when the GF_EOD command comes through.
  1233.  *
  1234.  *  - Filter's must pass GF_EOD they receive on to the next
  1235.  *    filter in the chain so it has the opportunity to flush
  1236.  *    any buffered data.
  1237.  *
  1238.  *  - All filters expect NVT end-of-lines.  The idea is to prepend
  1239.  *    or append either the gf_local_nvtnl or gf_nvtnl_local 
  1240.  *    os-dependant filters to the data on the appropriate end of the
  1241.  *    pipe for the task at hand.
  1242.  *
  1243.  *  - NOTE: As of 951004, filters no longer take their input as a single
  1244.  *    char argument, but rather get data to operate on via a vector
  1245.  *    representing the input queue in the FILTER_S structure.
  1246.  *
  1247.  */
  1248.  
  1249.  
  1250.  
  1251. /*
  1252.  * BASE64 TO BINARY encoding and decoding routines below
  1253.  */
  1254.  
  1255.  
  1256. /*
  1257.  * BINARY to BASE64 filter (encoding described in rfc1341)
  1258.  */
  1259. void
  1260. gf_binary_b64(f, flg)
  1261.     FILTER_S *f;
  1262.     int       flg;
  1263. {
  1264.     static char *v =
  1265.             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  1266.     GF_INIT(f, f->next);
  1267.  
  1268.     if(flg == GF_DATA){
  1269.     register unsigned char c;
  1270.     register unsigned char t = f->t;
  1271.     register long n = f->n;
  1272.  
  1273.     while(GF_GETC(f, c)){
  1274.  
  1275.         switch(n++){
  1276.           case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18:
  1277.           case 21: case 24: case 27: case 30: case 33: case 36: case 39:
  1278.           case 42: case 45:
  1279.         GF_PUTC(f->next, v[c >> 2]);
  1280.                     /* byte 1: high 6 bits (1) */
  1281.         t = c << 4;        /* remember high 2 bits for next */
  1282.         break;
  1283.  
  1284.           case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19:
  1285.           case 22: case 25: case 28: case 31: case 34: case 37: case 40:
  1286.           case 43:
  1287.         GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]);
  1288.         t = c << 2;
  1289.         break;
  1290.  
  1291.           case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20:
  1292.           case 23: case 26: case 29: case 32: case 35: case 38: case 41:
  1293.           case 44:
  1294.         GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]);
  1295.         GF_PUTC(f->next, v[c & 0x3f]);
  1296.         break;
  1297.         }
  1298.  
  1299.         if(n == 45){            /* start a new line? */
  1300.         GF_PUTC(f->next, '\015');
  1301.         GF_PUTC(f->next, '\012');
  1302.         n = 0L;
  1303.         }
  1304.     }
  1305.  
  1306.     f->n = n;
  1307.     f->t = t;
  1308.     GF_END(f, f->next);
  1309.     }
  1310.     else if(flg == GF_EOD){        /* no more data */
  1311.     switch (f->n % 3) {        /* handle trailing bytes */
  1312.       case 0:            /* no trailing bytes */
  1313.         break;
  1314.  
  1315.       case 1:
  1316.         GF_PUTC(f->next, v[(f->t) & 0x3f]);
  1317.         GF_PUTC(f->next, '=');    /* byte 3 */
  1318.         GF_PUTC(f->next, '=');    /* byte 4 */
  1319.         break;
  1320.  
  1321.       case 2:
  1322.         GF_PUTC(f->next, v[(f->t) & 0x3f]);
  1323.         GF_PUTC(f->next, '=');    /* byte 4 */
  1324.         break;
  1325.     }
  1326.  
  1327.     GF_FLUSH(f->next);
  1328.     (*f->next->f)(f->next, GF_EOD);
  1329.     }
  1330.     else if(flg == GF_RESET){
  1331.     dprint(9, (debugfile, "-- gf_reset binary_b64\n"));
  1332.     f->n = 0L;
  1333.     }
  1334. }
  1335.  
  1336.  
  1337.  
  1338. /*
  1339.  * BASE64 to BINARY filter (encoding described in rfc1341)
  1340.  */
  1341. void
  1342. gf_b64_binary(f, flg)
  1343.     FILTER_S *f;
  1344.     int       flg;
  1345. {
  1346.     static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
  1347.                65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
  1348.                65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63,
  1349.                52,53,54,55,56,57,58,59,60,61,62,65,65,64,65,65,
  1350.                65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
  1351.                15,16,17,18,19,20,21,22,23,24,25,65,65,65,65,65,
  1352.                65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
  1353.                41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65};
  1354.     GF_INIT(f, f->next);
  1355.  
  1356.     if(flg == GF_DATA){
  1357.     register unsigned char c;
  1358.     register unsigned char t = f->t;
  1359.     register int n = (int) f->n;
  1360.     register int state = f->f1;
  1361.  
  1362.     while(GF_GETC(f, c)){
  1363.  
  1364.         if(state){
  1365.         state = 0;
  1366.         if (c != '=') {
  1367.             gf_error("Illegal '=' in base64 text");
  1368.             /* NO RETURN */
  1369.         }
  1370.         }
  1371.  
  1372.         /* in range, and a valid value? */
  1373.         if((c & ~0x7f) || (c = v[c]) > 63){
  1374.         if(c == 64){
  1375.             switch (n++) {    /* check quantum position */
  1376.               case 2:
  1377.             state++;    /* expect an equal as next char */
  1378.             break;
  1379.  
  1380.               case 3:
  1381.             n = 0L;        /* restart quantum */
  1382.             break;
  1383.  
  1384.               default:        /* impossible quantum position */
  1385.             gf_error("Internal base64 decoder error");
  1386.             /* NO RETURN */
  1387.             }
  1388.         }
  1389.         }
  1390.         else{
  1391.         switch (n++) {        /* install based on quantum position */
  1392.           case 0:        /* byte 1: high 6 bits */
  1393.             t = c << 2;
  1394.             break;
  1395.  
  1396.           case 1:        /* byte 1: low 2 bits */
  1397.             GF_PUTC(f->next, (t|(c >> 4)));
  1398.             t = c << 4;        /* byte 2: high 4 bits */
  1399.             break;
  1400.  
  1401.           case 2:        /* byte 2: low 4 bits */
  1402.             GF_PUTC(f->next, (t|(c >> 2)));
  1403.             t = c << 6;        /* byte 3: high 2 bits */
  1404.             break;
  1405.  
  1406.           case 3:
  1407.             GF_PUTC(f->next, t | c);
  1408.             n = 0L;        /* reinitialize mechanism */
  1409.             break;
  1410.         }
  1411.         }
  1412.     }
  1413.  
  1414.     f->f1 = state;
  1415.     f->t = t;
  1416.     f->n = n;
  1417.     GF_END(f, f->next);
  1418.     }
  1419.     else if(flg == GF_EOD){
  1420.     GF_FLUSH(f->next);
  1421.     (*f->next->f)(f->next, GF_EOD);
  1422.     }
  1423.     else if(flg == GF_RESET){
  1424.     dprint(9, (debugfile, "-- gf_reset b64_binary\n"));
  1425.     f->n  = 0L;            /* quantum position */
  1426.     f->f1 = 0;            /* state holder: equal seen? */
  1427.     }
  1428. }
  1429.  
  1430.  
  1431.  
  1432.  
  1433. /*
  1434.  * QUOTED-PRINTABLE ENCODING AND DECODING filters below.
  1435.  * encoding described in rfc1341
  1436.  */
  1437.  
  1438. #define    GF_MAXLINE    80        /* good buffer size */
  1439.  
  1440. /*
  1441.  * default action for QUOTED-PRINTABLE to 8BIT decoder
  1442.  */
  1443. #define    GF_QP_DEFAULT(f, c)    { \
  1444.                     if((c) == ' '){ \
  1445.                     state = WSPACE; \
  1446.                         /* reset white space! */ \
  1447.                     (f)->linep = (f)->line; \
  1448.                     *((f)->linep)++ = ' '; \
  1449.                     } \
  1450.                     else if((c) == '='){ \
  1451.                     state = EQUAL; \
  1452.                     } \
  1453.                     else \
  1454.                       GF_PUTC((f)->next, (c)); \
  1455.                 }
  1456.  
  1457.  
  1458. /*
  1459.  * QUOTED-PRINTABLE to 8BIT filter
  1460.  */
  1461. void
  1462. gf_qp_8bit(f, flg)
  1463.     FILTER_S *f;
  1464.     int       flg;
  1465. {
  1466.     GF_INIT(f, f->next);
  1467.  
  1468.     if(flg == GF_DATA){
  1469.     register unsigned char c;
  1470.     register int state = f->f1;
  1471.  
  1472.     while(GF_GETC(f, c)){
  1473.  
  1474.         switch(state){
  1475.           case DFL :        /* default case */
  1476.           default:
  1477.         GF_QP_DEFAULT(f, c);
  1478.         break;
  1479.  
  1480.           case CCR    :        /* non-significant space */
  1481.         state = DFL;
  1482.         if(c == '\012')
  1483.           continue;        /* go on to next char */
  1484.  
  1485.         GF_QP_DEFAULT(f, c);
  1486.         break;
  1487.  
  1488.           case EQUAL  :
  1489.         if(c == '\015'){    /* "=\015" is a soft EOL */
  1490.             state = CCR;
  1491.             break;
  1492.         }
  1493.  
  1494.         if(c == '='){        /* compatibility clause for old guys */
  1495.             GF_PUTC(f->next, '=');
  1496.             state = DFL;
  1497.             break;
  1498.         }
  1499.  
  1500.         if(!isxdigit(c)){    /* must be hex! */
  1501.             fs_give((void **)&(f->line));
  1502.             gf_error("Non-hexadecimal character in QP encoding");
  1503.             /* NO RETURN */
  1504.         }
  1505.  
  1506.         if (isdigit (c)) 
  1507.           f->t = c - '0';
  1508.         else
  1509.           f->t = c - (isupper (c) ? 'A' - 10 : 'a' - 10);
  1510.  
  1511.         state = HEX;
  1512.         break;
  1513.  
  1514.           case HEX :
  1515.         state = DFL;
  1516.         if(!isxdigit(c)){    /* must be hex! */
  1517.             fs_give((void **)&(f->line));
  1518.             gf_error("Non-hexadecimal character in QP encoding");
  1519.             /* NO RETURN */
  1520.         }
  1521.  
  1522.         if (isdigit(c)) 
  1523.           c -= '0';
  1524.         else
  1525.           c -= (isupper (c) ? 'A' - 10 : 'a' - 10);
  1526.  
  1527.         GF_PUTC(f->next, c + (f->t << 4));
  1528.         break;
  1529.  
  1530.           case WSPACE :
  1531.         if(c == ' '){        /* toss it in with other spaces */
  1532.             if(f->linep - f->line < GF_MAXLINE)
  1533.               *(f->linep)++ = ' ';
  1534.             break;
  1535.         }
  1536.  
  1537.         state = DFL;
  1538.         if(c == '\015'){    /* not our white space! */
  1539.             f->linep = f->line;    /* reset buffer */
  1540.             GF_PUTC(f->next, '\015');
  1541.             break;
  1542.         }
  1543.  
  1544.         /* the spaces are ours, write 'em */
  1545.         f->n = f->linep - f->line;
  1546.         while((f->n)--)
  1547.           GF_PUTC(f->next, ' ');
  1548.  
  1549.         GF_QP_DEFAULT(f, c);    /* take care of 'c' in default way */
  1550.         break;
  1551.         }
  1552.     }
  1553.  
  1554.     f->f1 = state;
  1555.     GF_END(f, f->next);
  1556.     }
  1557.     else if(flg == GF_EOD){
  1558.     fs_give((void **)&(f->line));
  1559.     GF_FLUSH(f->next);
  1560.     (*f->next->f)(f->next, GF_EOD);
  1561.     }
  1562.     else if(flg == GF_RESET){
  1563.     dprint(9, (debugfile, "-- gf_reset qp_8bit\n"));
  1564.     f->f1 = DFL;
  1565.     f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
  1566.     }
  1567. }
  1568.  
  1569.  
  1570.  
  1571. /*
  1572.  * USEFUL MACROS TO HELP WITH QP ENCODING
  1573.  */
  1574.  
  1575. #define    QP_MAXL    75            /* 76th place only for continuation */
  1576.  
  1577. /*
  1578.  * Macro to test and wrap long quoted printable lines
  1579.  */
  1580. #define    GF_8BIT_WRAP(f, c)    { \
  1581.                     GF_PUTC((f)->next, '='); \
  1582.                     GF_PUTC((f)->next, '\015'); \
  1583.                     GF_PUTC((f)->next, '\012'); \
  1584.                 }
  1585.  
  1586. /*
  1587.  * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line
  1588.  * if needed.
  1589.  */
  1590. #define    GF_8BIT_PUT(f, c)    { \
  1591.                      if((++(f->n)) > QP_MAXL){ \
  1592.                      GF_8BIT_WRAP((f), (c)); \
  1593.                      f->n = 1; \
  1594.                      } \
  1595.                      GF_PUTC(f->next, c); \
  1596.                 }
  1597.  
  1598.  
  1599. /*
  1600.  * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft
  1601.  * line break if needed.
  1602.  */
  1603. #define    GF_8BIT_PUT_QUOTE(f, c)    { \
  1604.                     if(((f)->n += 3) > QP_MAXL){ \
  1605.                     GF_8BIT_WRAP((f), (c)); \
  1606.                     (f)->n = 3;    /* set line count */ \
  1607.                     } \
  1608.                     GF_PUTC((f)->next, '='); \
  1609.                         /* high order 4 bits */ \
  1610.                     GF_PUTC((f)->next, hex[(c) >> 4]); \
  1611.                         /* low order 4 bits */ \
  1612.                     GF_PUTC((f)->next, hex[(c) & 0xf]); \
  1613.                 }
  1614.  
  1615. /*
  1616.  * default action for 8bit to quoted printable encoder
  1617.  */
  1618. #define    GF_8BIT_DEFAULT(f, c)    if((c) == ' '){ \
  1619.                     state = WSPACE; \
  1620.                 } \
  1621.                 else if(c == '\015'){ \
  1622.                     state = CCR; \
  1623.                 } \
  1624.                 else if(iscntrl(c & 0x7f) || (c == 0x7f) \
  1625.                     || (c & 0x80) || (c == '=') \
  1626.                     || (c == '.' && f->n == 0L)){ \
  1627.                     GF_8BIT_PUT_QUOTE(f, c); \
  1628.                 } \
  1629.                 else{ \
  1630.                   GF_8BIT_PUT(f, c); \
  1631.                 }
  1632.  
  1633.  
  1634. /*
  1635.  * 8BIT to QUOTED-PRINTABLE filter
  1636.  */
  1637. void
  1638. gf_8bit_qp(f, flg)
  1639.     FILTER_S *f;
  1640.     int       flg;
  1641. {
  1642.     static char *hex = "0123456789ABCDEF";
  1643.     short dummy_dots = 0, dummy_dmap = 1;
  1644.     GF_INIT(f, f->next);
  1645.  
  1646.     if(flg == GF_DATA){
  1647.     register unsigned char c;
  1648.     register int state = f->f1;
  1649.  
  1650.     while(GF_GETC(f, c)){
  1651.  
  1652.         /* keep track of "^JFrom " */
  1653.         Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c);
  1654.  
  1655.         switch(state){
  1656.           case DFL :        /* handle ordinary case */
  1657.         GF_8BIT_DEFAULT(f, c);
  1658.         break;
  1659.  
  1660.           case CCR :        /* true line break? */
  1661.         state = DFL;
  1662.         if(c == '\012'){
  1663.             GF_PUTC(f->next, '\015');
  1664.             GF_PUTC(f->next, '\012');
  1665.             f->n = 0L;
  1666.         }
  1667.         else{            /* nope, quote the CR */
  1668.             GF_8BIT_PUT_QUOTE(f, '\015');
  1669.             GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */
  1670.         }
  1671.         break;
  1672.  
  1673.           case WSPACE:
  1674.         state = DFL;
  1675.         if(c == '\015' || f->t){ /* handle the space */
  1676.             GF_8BIT_PUT_QUOTE(f, ' ');
  1677.             f->t = 0;        /* reset From flag */
  1678.         }
  1679.         else
  1680.           GF_8BIT_PUT(f, ' ');
  1681.  
  1682.         GF_8BIT_DEFAULT(f, c);    /* handle 'c' in the default way */
  1683.         break;
  1684.         }
  1685.     }
  1686.  
  1687.     f->f1 = state;
  1688.     GF_END(f, f->next);
  1689.     }
  1690.     else if(flg == GF_EOD){
  1691.     switch(f->f1){
  1692.       case CCR :
  1693.         GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */
  1694.         break;
  1695.  
  1696.       case WSPACE :
  1697.         GF_8BIT_PUT_QUOTE(f, ' ');    /* write the last space */
  1698.         break;
  1699.     }
  1700.  
  1701.     GF_FLUSH(f->next);
  1702.     (*f->next->f)(f->next, GF_EOD);
  1703.     }
  1704.     else if(flg == GF_RESET){
  1705.     dprint(9, (debugfile, "-- gf_reset 8bit_qp\n"));
  1706.     f->f1 = DFL;            /* state from last character        */
  1707.     f->f2 = 1;            /* state of "^NFrom " bitmap        */
  1708.     f->t  = 0;
  1709.     f->n  = 0L;            /* number of chars in current line  */
  1710.     }
  1711. }
  1712.  
  1713.  
  1714.  
  1715. /*
  1716.  * RICHTEXT-TO-PLAINTEXT filter
  1717.  */
  1718.  
  1719. /*
  1720.  * option to be used by rich2plain (NOTE: if this filter is ever 
  1721.  * used more than once in a pipe, all instances will have the same
  1722.  * option value)
  1723.  */
  1724. static int gf_rich_plain = 0;
  1725.  
  1726.  
  1727. /*----------------------------------------------------------------------
  1728.       richtext to plaintext filter
  1729.     
  1730.  Args: f -- 
  1731.        flg  --
  1732.  
  1733.   This basically removes all richtext formatting. A cute hack is used 
  1734.   to get bold and underlining to work.
  1735.   Further work could be done to handle things like centering and right
  1736.   and left flush, but then it could no longer be done in place. This
  1737.   operates on text *with* CRLF's.
  1738.  
  1739.   WARNING: does not wrap lines!
  1740.  ----*/
  1741. void
  1742. gf_rich2plain(f, flg)
  1743.     FILTER_S *f;
  1744.     int       flg;
  1745. {
  1746. /* BUG: qoute incoming \255 values */
  1747.     GF_INIT(f, f->next);
  1748.  
  1749.     if(flg == GF_DATA){
  1750.     register unsigned char c;
  1751.     register int state = f->f1;
  1752.  
  1753.     while(GF_GETC(f, c)){
  1754.  
  1755.         switch(state){
  1756.           case TOKEN :        /* collect a richtext token */
  1757.         if(c == '>'){        /* what should we do with it? */
  1758.             state       = DFL;    /* return to default next time */
  1759.             *(f->linep) = '\0';    /* cap off token */
  1760.             if(f->line[0] == 'l' && f->line[1] == 't'){
  1761.             GF_PUTC(f->next, '<'); /* literal '<' */
  1762.             }
  1763.             else if(f->line[0] == 'n' && f->line[1] == 'l'){
  1764.             GF_PUTC(f->next, '\015');/* newline! */
  1765.             GF_PUTC(f->next, '\012');
  1766.             }
  1767.             else if(!strcmp("comment", f->line)){
  1768.             (f->f2)++;
  1769.             }
  1770.             else if(!strcmp("/comment", f->line)){
  1771.             f->f2 = 0;
  1772.             }
  1773.             else if(!strcmp("/paragraph", f->line)) {
  1774.             GF_PUTC(f->next, '\r');
  1775.             GF_PUTC(f->next, '\n');
  1776.             GF_PUTC(f->next, '\r');
  1777.             GF_PUTC(f->next, '\n');
  1778.             }
  1779.             else if(!gf_rich_plain){
  1780.             if(!strcmp(f->line, "bold")) {
  1781.                 GF_PUTC(f->next, TAG_EMBED);
  1782.                 GF_PUTC(f->next, TAG_BOLDON);
  1783.             } else if(!strcmp(f->line, "/bold")) {
  1784.                 GF_PUTC(f->next, TAG_EMBED);
  1785.                 GF_PUTC(f->next, TAG_BOLDOFF);
  1786.             } else if(!strcmp(f->line, "italic")) {
  1787.                 GF_PUTC(f->next, TAG_EMBED);
  1788.                 GF_PUTC(f->next, TAG_ULINEON);
  1789.             } else if(!strcmp(f->line, "/italic")) {
  1790.                 GF_PUTC(f->next, TAG_EMBED);
  1791.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1792.             } else if(!strcmp(f->line, "underline")) {
  1793.                 GF_PUTC(f->next, TAG_EMBED);
  1794.                 GF_PUTC(f->next, TAG_ULINEON);
  1795.             } else if(!strcmp(f->line, "/underline")) {
  1796.                 GF_PUTC(f->next, TAG_EMBED);
  1797.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1798.             } 
  1799.             }
  1800.             /* else we just ignore the token! */
  1801.  
  1802.             f->linep = f->line;    /* reset token buffer */
  1803.         }
  1804.         else{            /* add char to token */
  1805.             if(f->linep - f->line > 40){
  1806.             /* What? rfc1341 says 40 char tokens MAX! */
  1807.             fs_give((void **)&(f->line));
  1808.             gf_error("Richtext token over 40 characters");
  1809.             /* NO RETURN */
  1810.             }
  1811.         
  1812.             *(f->linep)++ = isupper(c) ? c - 'A' + 'a' : c;
  1813.         }
  1814.         break;
  1815.  
  1816.           case CCR   :
  1817.         state = DFL;        /* back to default next time */
  1818.         if(c == '\012'){    /* treat as single space?    */
  1819.             GF_PUTC(f->next, ' ');
  1820.             break;
  1821.         }
  1822.         /* fall thru to process c */
  1823.  
  1824.           case DFL   :
  1825.           default:
  1826.         if(c == '<')
  1827.           state = TOKEN;
  1828.         else if(c == '\015')
  1829.           state = CCR;
  1830.         else if(!f->f2)        /* not in comment! */
  1831.           GF_PUTC(f->next, c);
  1832.  
  1833.         break;
  1834.         }
  1835.     }
  1836.  
  1837.     f->f1 = state;
  1838.     GF_END(f, f->next);
  1839.     }
  1840.     else if(flg == GF_EOD){
  1841.     if(f->f1 = (f->linep != f->line)){
  1842.         /* incomplete token!! */
  1843.         gf_error("Incomplete token in richtext");
  1844.         /* NO RETURN */
  1845.     }
  1846.  
  1847.     fs_give((void **)&(f->line));
  1848.     GF_FLUSH(f->next);
  1849.     (*f->next->f)(f->next, GF_EOD);
  1850.     }
  1851.     else if(flg == GF_RESET){
  1852.     dprint(9, (debugfile, "-- gf_reset rich2plain\n"));
  1853.     f->f1 = DFL;            /* state */
  1854.     f->f2 = 0;            /* set means we're in a comment */
  1855.     f->linep = f->line = (char *)fs_get(45 * sizeof(char));
  1856.     }
  1857. }
  1858.  
  1859.  
  1860. /*
  1861.  * function called from the outside to set
  1862.  * richtext filter's options
  1863.  */
  1864. void
  1865. gf_rich2plain_opt(plain)
  1866.     int plain;
  1867. {
  1868.     gf_rich_plain = plain;
  1869. }
  1870.  
  1871.  
  1872.  
  1873. /*
  1874.  * ENRICHED-TO-PLAIN text filter
  1875.  */
  1876.  
  1877. static int gf_enriched_plain = 0;
  1878.  
  1879.  
  1880. /*----------------------------------------------------------------------
  1881.       enriched text to plain text filter (ala rfc1523)
  1882.     
  1883.  Args: f -- state and input data
  1884.        flg -- 
  1885.  
  1886.   This basically removes all enriched formatting. A cute hack is used 
  1887.   to get bold and underlining to work.
  1888.  
  1889.   Further work could be done to handle things like centering and right
  1890.   and left flush, but then it could no longer be done in place. This
  1891.   operates on text *with* CRLF's.
  1892.  
  1893.   WARNING: does not wrap lines!
  1894.  ----*/
  1895. void
  1896. gf_enriched2plain(f, flg)
  1897.     FILTER_S *f;
  1898.     int       flg;
  1899. {
  1900. /* BUG: qoute incoming \255 values */
  1901.     GF_INIT(f, f->next);
  1902.  
  1903.     if(flg == GF_DATA){
  1904.     register unsigned char c;
  1905.     register int state = f->f1;
  1906.  
  1907.     while(GF_GETC(f, c)){
  1908.  
  1909.         switch(state){
  1910.           case TOKEN :        /* collect a richtext token */
  1911.         if(c == '>'){        /* what should we do with it? */
  1912.             state       = DFL;    /* return to default next time */
  1913.             *(f->linep) = '\0';    /* cap off token */
  1914.             if(!strcmp("param", f->line)){
  1915.             (f->f2)++;
  1916.             }
  1917.             else if(!strcmp("/param", f->line)){
  1918.             f->f2 = 0;
  1919.             }
  1920.             else if(!gf_enriched_plain){
  1921.             /* Following is a cute hack or two to get 
  1922.                bold and underline on the screen. 
  1923.                See Putline0n() where these codes are
  1924.                interpreted */
  1925.             if(!strcmp(f->line, "bold")) {
  1926.                 GF_PUTC(f->next, TAG_EMBED);
  1927.                 GF_PUTC(f->next, TAG_BOLDON);
  1928.             } else if(!strcmp(f->line, "/bold")) {
  1929.                 GF_PUTC(f->next, TAG_EMBED);
  1930.                 GF_PUTC(f->next, TAG_BOLDOFF);
  1931.             } else if(!strcmp(f->line, "italic")) {
  1932.                 GF_PUTC(f->next, TAG_EMBED);
  1933.                 GF_PUTC(f->next, TAG_ULINEON);
  1934.             } else if(!strcmp(f->line, "/italic")) {
  1935.                 GF_PUTC(f->next, TAG_EMBED);
  1936.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1937.             } else if(!strcmp(f->line, "underline")) {
  1938.                 GF_PUTC(f->next, TAG_EMBED);
  1939.                 GF_PUTC(f->next, TAG_ULINEON);
  1940.             } else if(!strcmp(f->line, "/underline")) {
  1941.                 GF_PUTC(f->next, TAG_EMBED);
  1942.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1943.             } 
  1944.             }
  1945.             /* else we just ignore the token! */
  1946.  
  1947.             f->linep = f->line;    /* reset token buffer */
  1948.         }
  1949.         else if(c == '<'){        /* literal '<'? */
  1950.             if(f->linep == f->line){
  1951.             GF_PUTC(f->next, '<');
  1952.             state = DFL;
  1953.             }
  1954.             else{
  1955.             fs_give((void **)&(f->line));
  1956.             gf_error("Malformed Enriched text: unexpected '<'");
  1957.             /* NO RETURN */
  1958.             }
  1959.         }
  1960.         else{            /* add char to token */
  1961.             if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */
  1962.             fs_give((void **)&(f->line));
  1963.             gf_error("Malformed Enriched text: token too long");
  1964.             /* NO RETURN */
  1965.             }
  1966.         
  1967.             *(f->linep)++ = isupper(c) ? c - 'A' + 'a' : c;
  1968.         }
  1969.         break;
  1970.  
  1971.           case CCR   :
  1972.         if(c != '\012'){    /* treat as single space?    */
  1973.             state = DFL;    /* lone cr? */
  1974.             f->f2 = 0;
  1975.             GF_PUTC(f->next, '\015');
  1976.             goto df;
  1977.         }
  1978.  
  1979.         state = CLF;
  1980.         break;
  1981.  
  1982.           case CLF   :
  1983.         if(c == '\015'){    /* treat as single space?    */
  1984.             state = CCR;    /* repeat crlf's mean real newlines */
  1985.             f->f2 = 1;
  1986.             GF_PUTC(f->next, '\r');
  1987.             GF_PUTC(f->next, '\n');
  1988.             break;
  1989.         }
  1990.         else{
  1991.             state = DFL;
  1992.             if(!f->f2)
  1993.               GF_PUTC(f->next, ' ');
  1994.  
  1995.             f->f2 = 0;
  1996.         }
  1997.  
  1998.         /* fall thru to take care of 'c' */
  1999.  
  2000.           case DFL   :
  2001.           default :
  2002.           df : 
  2003.         if(c == '<')
  2004.           state = TOKEN;
  2005.         else if(c == '\015')
  2006.           state = CCR;
  2007.         else if(!f->f2)        /* not in param! */
  2008.           GF_PUTC(f->next, c);
  2009.  
  2010.         break;
  2011.         }
  2012.     }
  2013.  
  2014.     f->f1 = state;
  2015.     GF_END(f, f->next);
  2016.     }
  2017.     else if(flg == GF_EOD){
  2018.     if(f->f1 = (f->linep != f->line)){
  2019.         /* incomplete token!! */
  2020.         gf_error("Incomplete token in richtext");
  2021.         /* NO RETURN */
  2022.     }
  2023.  
  2024.     fs_give((void **)&(f->line));
  2025.  
  2026.     GF_FLUSH(f->next);
  2027.     (*f->next->f)(f->next, GF_EOD);
  2028.     }
  2029.     else if(flg == GF_RESET){
  2030.     dprint(9, (debugfile, "-- gf_reset enriched2plain\n"));
  2031.     f->f1 = DFL;            /* state */
  2032.     f->f2 = 0;            /* set means we're in a comment */
  2033.     f->linep = f->line = (char *)fs_get(65 * sizeof(char));
  2034.     }
  2035. }
  2036.  
  2037.  
  2038. /*
  2039.  * function called from the outside to set
  2040.  * richtext filter's options
  2041.  */
  2042. void
  2043. gf_enriched2plain_opt(plain)
  2044.     int plain;
  2045. {
  2046.     gf_enriched_plain = plain;
  2047. }
  2048.  
  2049.  
  2050.  
  2051. /*
  2052.  * HTML-TO-PLAIN text filter
  2053.  */
  2054.  
  2055. /*----------------------------------------------------------------------
  2056.   HTML text to plain text filter (ala HTML 2.0)
  2057.     
  2058.  
  2059.   This basically tries to do the best it can with HTML 2.0, level 1
  2060.   text formatting.
  2061.  
  2062.  ----*/
  2063. void
  2064. gf_html2plain(f, flg)
  2065.     FILTER_S *f;
  2066.     int       flg;
  2067. {
  2068.     /* place holder for filter under development */
  2069. }
  2070.  
  2071.  
  2072. /*
  2073.  * ESCAPE CODE FILTER - remove unknown and possibly dangerous escape codes
  2074.  * from the text stream.
  2075.  */
  2076.  
  2077. #define    MAX_ESC_LEN    5
  2078.  
  2079. /*
  2080.  * the simple filter, removes unknown escape codes from the stream
  2081.  */
  2082. void
  2083. gf_escape_filter(f, flg)
  2084.     FILTER_S *f;
  2085.     int       flg;
  2086. {
  2087.     register char *p;
  2088.     GF_INIT(f, f->next);
  2089.  
  2090.     if(flg == GF_DATA){
  2091.     register unsigned char c;
  2092.     register int state = f->f1;
  2093.  
  2094.     while(GF_GETC(f, c)){
  2095.  
  2096.         if(state){
  2097.         if(c == '\033' || f->n == MAX_ESC_LEN){
  2098.             f->line[f->n] = '\0';
  2099.             f->n = 0L;
  2100.             if(!match_escapes(f->line)){
  2101.             GF_PUTC(f->next, '^');
  2102.             GF_PUTC(f->next, '[');
  2103.             }
  2104.             else
  2105.               GF_PUTC(f->next, '\033');
  2106.  
  2107.             p = f->line;
  2108.             while(*p)
  2109.               GF_PUTC(f->next, *p++);
  2110.  
  2111.             if(c == '\033')
  2112.               continue;
  2113.             else
  2114.               state = 0;            /* fall thru */
  2115.         }
  2116.         else{
  2117.             f->line[f->n++] = c;        /* collect */
  2118.             continue;
  2119.         }
  2120.         }
  2121.  
  2122.         if(c == '\033')
  2123.           state = 1;
  2124.         else
  2125.           GF_PUTC(f->next, c);
  2126.     }
  2127.  
  2128.     f->f1 = state;
  2129.     GF_END(f, f->next);
  2130.     }
  2131.     else if(flg == GF_EOD){
  2132.     if(f->f1){
  2133.         if(!match_escapes(f->line)){
  2134.         GF_PUTC(f->next, '^');
  2135.         GF_PUTC(f->next, '[');
  2136.         }
  2137.         else
  2138.           GF_PUTC(f->next, '\033');
  2139.     }
  2140.  
  2141.     for(p = f->line; f->n; f->n--, p++)
  2142.       GF_PUTC(f->next, *p);
  2143.  
  2144.     fs_give((void **)&(f->line));    /* free temp line buffer */
  2145.     GF_FLUSH(f->next);
  2146.     (*f->next->f)(f->next, GF_EOD);
  2147.     }
  2148.     else if(flg == GF_RESET){
  2149.     dprint(9, (debugfile, "-- gf_reset escape\n"));
  2150.     f->f1    = 0;
  2151.     f->n     = 0L;
  2152.     f->linep = f->line = (char *)fs_get((MAX_ESC_LEN + 1) * sizeof(char));
  2153.     }
  2154. }
  2155.  
  2156.  
  2157.  
  2158. /*
  2159.  * CONTROL CHARACTER FILTER - transmogrify control characters into their
  2160.  * corresponding string representations (you know, ^blah and such)...
  2161.  */
  2162.  
  2163. /*
  2164.  * the simple filter transforms unknown control characters in the stream
  2165.  * into harmless strings.
  2166.  */
  2167. void
  2168. gf_control_filter(f, flg)
  2169.     FILTER_S *f;
  2170.     int       flg;
  2171. {
  2172.     register char *p;
  2173.     GF_INIT(f, f->next);
  2174.  
  2175.     if(flg == GF_DATA){
  2176.     register unsigned char c;
  2177.  
  2178.     while(GF_GETC(f, c)){
  2179.  
  2180.         if(iscntrl(c & 0x7f)
  2181.            && !(isspace(c) || c == '\016' || c == '\017' || c == '\033')){
  2182.         GF_PUTC(f->next, '^');
  2183.         GF_PUTC(f->next, c + '@');
  2184.         }
  2185.         else
  2186.           GF_PUTC(f->next, c);
  2187.     }
  2188.  
  2189.     GF_END(f, f->next);
  2190.     }
  2191.     else if(flg == GF_EOD){
  2192.     GF_FLUSH(f->next);
  2193.     (*f->next->f)(f->next, GF_EOD);
  2194.     }
  2195. }
  2196.  
  2197.  
  2198.  
  2199. /*
  2200.  * LINEWRAP FILTER - insert CRLF's at end of nearest whitespace before
  2201.  * specified line width
  2202.  */
  2203.  
  2204. /*
  2205.  * option to be used by gf_wrap_filter (NOTE: if this filter is ever 
  2206.  * used more than once in a pipe, all instances will have the same
  2207.  * option value)
  2208.  */
  2209. static int gf_wrap_width = 75;
  2210.  
  2211.  
  2212. /*
  2213.  * the simple filter, breaks lines at end of white space nearest
  2214.  * to global "gf_wrap_width" in length
  2215.  */
  2216. void
  2217. gf_wrap(f, flg)
  2218.     FILTER_S *f;
  2219.     int       flg;
  2220. {
  2221.     register long i;
  2222.     register char *breakp, *tp;
  2223.     GF_INIT(f, f->next);
  2224.  
  2225.     if(flg == GF_DATA){
  2226.     register unsigned char c;
  2227.     register int state = f->f1;
  2228.  
  2229.     while(GF_GETC(f, c)){
  2230.  
  2231.         switch(state){
  2232.           case CCR:
  2233.         state = DFL;
  2234.         if(c == '\012'){
  2235.             for(tp = f->line; tp < f->linep; tp++)
  2236.               GF_PUTC(f->next, *tp);
  2237.  
  2238.             GF_PUTC(f->next, '\015');
  2239.             GF_PUTC(f->next, '\012');
  2240.             f->n = 0L;
  2241.             f->linep = f->line;
  2242.             break;
  2243.         }
  2244.         else{
  2245.             *(f->linep)++ = '\015';    /* shouldn't happen often! */
  2246.             (f->n)++;
  2247.         }
  2248.         /* else fall thru to take care of c */
  2249.  
  2250.           case DFL:
  2251.         if(c == '\015'){        /* already has newline? */
  2252.             state = CCR;
  2253.             break;
  2254.         }
  2255.         else if(c == '\011'){    /* account for tabs too! */
  2256.             i = f->n;
  2257.             while((++i)&0x07)
  2258.               ;
  2259.  
  2260.             i -= f->n;
  2261.         }
  2262.         else
  2263.           i = 1;
  2264.  
  2265.         if(f->n + i > (long)gf_wrap_width){ /* wrap? */
  2266.             for(breakp = &f->linep[-1]; breakp >= f->line; breakp--)
  2267.               if(isspace(*breakp))
  2268.             break;
  2269.  
  2270.             f->n = i = 0;
  2271.             for(tp = f->line; tp < f->linep; tp++){
  2272.             if(breakp < f->line || tp <= breakp)
  2273.               GF_PUTC(f->next, *tp); /* write the line */
  2274.             else{        /* shift it back */
  2275.                 i = tp - breakp - 1;
  2276.                 if((f->line[i++] = *tp) == '\011')
  2277.                   while((++(f->n))&0x07);
  2278.                 else
  2279.                   (f->n)++;
  2280.             }
  2281.             }
  2282.  
  2283.             GF_PUTC(f->next, '\015');
  2284.             GF_PUTC(f->next, '\012');
  2285.             f->linep = &f->line[i];    /* reset f->linep */
  2286.         }
  2287.  
  2288.         if((*(f->linep)++ = c) == '\011')
  2289.           while((++(f->n))&0x07);
  2290.         else
  2291.           (f->n)++;
  2292.  
  2293.         break;
  2294.         }
  2295.     }
  2296.  
  2297.     f->f1 = state;
  2298.     GF_END(f, f->next);
  2299.     }
  2300.     else if(flg == GF_EOD){
  2301.     for(i = 0; i < f->n; i++)    /* flush the remaining line */
  2302.       GF_PUTC(f->next, f->line[i]);
  2303.  
  2304.     fs_give((void **)&(f->line));    /* free temp line buffer */
  2305.     GF_FLUSH(f->next);
  2306.     (*f->next->f)(f->next, GF_EOD);
  2307.     }
  2308.     else if(flg == GF_RESET){
  2309.     dprint(9, (debugfile, "-- gf_reset wrap\n"));
  2310.     f->f1    = DFL;
  2311.     f->n     = 0L;
  2312.     f->linep = f->line = (char *)fs_get((gf_wrap_width+10)*sizeof(char));
  2313.     }
  2314. }
  2315.  
  2316.  
  2317. /*
  2318.  * function called from the outside to set
  2319.  * wrap filter's width option
  2320.  */
  2321. void
  2322. gf_wrap_filter_opt(width)
  2323.     int width;
  2324. {
  2325.     gf_wrap_width = width;
  2326. }
  2327.  
  2328. /*
  2329.  * LINE PREFIX FILTER - insert given text at beginning of each
  2330.  * line
  2331.  */
  2332.  
  2333. /*
  2334.  * option to be used by gf_prefix
  2335.  */
  2336. static char *gf_prefix_prefix = NULL;
  2337.  
  2338. #define    GF_PREFIX_WRITE(s)    { \
  2339.                     register char *p; \
  2340.                     if(p = (s)) \
  2341.                       while(*p) \
  2342.                     GF_PUTC(f->next, *p++); \
  2343.                 }
  2344.  
  2345.  
  2346. /*
  2347.  * the simple filter, prepends each line with the requested prefix.
  2348.  * if prefix is null, does nothing, and as with all filters, assumes
  2349.  * NVT end of lines.
  2350.  */
  2351. void
  2352. gf_prefix(f, flg)
  2353.     FILTER_S *f;
  2354.     int       flg;
  2355. {
  2356.     GF_INIT(f, f->next);
  2357.  
  2358.     if(flg == GF_DATA){
  2359.     register unsigned char c;
  2360.     register int state = f->f1;
  2361.     register int first = f->f2;
  2362.  
  2363.     while(GF_GETC(f, c)){
  2364.  
  2365.         if(first){            /* write initial prefix!! */
  2366.         first = 0;        /* but just once */
  2367.         GF_PREFIX_WRITE((char *) f->data);
  2368.         }
  2369.         else if(state){
  2370.         state = 0;
  2371.         GF_PUTC(f->next, '\015');
  2372.         if(c == '\012'){
  2373.             GF_PUTC(f->next, '\012');
  2374.             GF_PREFIX_WRITE((char *) f->data);
  2375.             continue;
  2376.         }
  2377.         /* else fall thru to handle 'c' */
  2378.         }
  2379.  
  2380.         if(c == '\015')        /* already has newline? */
  2381.           state = 1;
  2382.         else
  2383.           GF_PUTC(f->next, c);
  2384.     }
  2385.  
  2386.     f->f1 = state;
  2387.     f->f2 = first;
  2388.     GF_END(f, f->next);
  2389.     }
  2390.     else if(flg == GF_EOD){
  2391.     GF_FLUSH(f->next);
  2392.     (*f->next->f)(f->next, GF_EOD);
  2393.     }
  2394.     else if(flg == GF_RESET){
  2395.     dprint(9, (debugfile, "-- gf_reset prefix\n"));
  2396.     f->f1   = 0;
  2397.     f->f2   = 1;            /* nothing written yet */
  2398.     f->data = gf_prefix_prefix;
  2399.     }
  2400. }
  2401.  
  2402.  
  2403. /*
  2404.  * function called from the outside to set
  2405.  * prefix filter's prefix string
  2406.  */
  2407. void
  2408. gf_prefix_opt(prefix)
  2409.     char *prefix;
  2410. {
  2411.     gf_prefix_prefix = prefix;
  2412. }
  2413.  
  2414.  
  2415. /*
  2416.  * LINE TEST FILTER - accumulate lines and offer each to the provided
  2417.  * test function.
  2418.  */
  2419.  
  2420.  
  2421. /*
  2422.  * option to be used by gf_line_test
  2423.  */
  2424. static int (*gf_line_test_f) PROTO((long, char *));
  2425.  
  2426. /* accumulator growth increment */
  2427. #define    LINE_TEST_BLOCK    1024
  2428.  
  2429. #define    GF_LINE_TEST_EOB(f)    ((f)->line + ((f)->f2 - 1))
  2430.  
  2431. #define    GF_LINE_TEST_FLUSH(f)    { \
  2432.                     register char *op; \
  2433.                     for(op = (f)->line; op < p; op++) \
  2434.                       GF_PUTC((f)->next, *op); \
  2435.                     p = (f)->line; \
  2436.                 }
  2437.  
  2438.  
  2439. #define    GF_LINE_TEST_ADD(f, c)    { \
  2440.                     if(p >= eobuf){ \
  2441.                     f->f2 += LINE_TEST_BLOCK; \
  2442.                     fs_resize((void **)&f->line, \
  2443.                           (size_t) f->f2 * sizeof(char)); \
  2444.                     eobuf = GF_LINE_TEST_EOB(f); \
  2445.                     p = eobuf - LINE_TEST_BLOCK; \
  2446.                     } \
  2447.                     *p++ = c; \
  2448.                 }
  2449.  
  2450.  
  2451. /*
  2452.  * this simple filter accumulates characters until a newline, offers it
  2453.  * to the provided test function, and then passes it on.  It assumes
  2454.  * NVT EOLs.
  2455.  */
  2456. void
  2457. gf_line_test(f, flg)
  2458.     FILTER_S *f;
  2459.     int          flg;
  2460. {
  2461.     register char *p = f->linep;
  2462.     register char *eobuf = GF_LINE_TEST_EOB(f);
  2463.     GF_INIT(f, f->next);
  2464.  
  2465.     if(flg == GF_DATA){
  2466.     register unsigned char c;
  2467.     register int state = f->f1;
  2468.  
  2469.     while(GF_GETC(f, c)){
  2470.  
  2471.         if(state){
  2472.         state = 0;
  2473.         if(c == '\012'){
  2474.             int done;
  2475.  
  2476.             *p = '\0';
  2477.             done = (*((int (*)()) f->data))(f->n++, f->line);
  2478.             GF_LINE_TEST_FLUSH(f);
  2479.             GF_PUTC(f->next, '\015');
  2480.             GF_PUTC(f->next, '\012');
  2481.             /*
  2482.              * if the line tester returns TRUE, it's
  2483.              * telling us its seen enough and doesn't
  2484.              * want to see any more.  Remove ourself 
  2485.              * from the pipeline...
  2486.              */
  2487.             if(done){
  2488.             if(gf_master == f){
  2489.                 gf_master = f->next;
  2490.             }
  2491.             else{
  2492.                 FILTER_S *fprev;
  2493.  
  2494.                 for(fprev = gf_master;
  2495.                 fprev && fprev->next != f;
  2496.                 fprev = fprev->next)
  2497.                   ;
  2498.  
  2499.                 if(fprev)        /* wha??? */
  2500.                   fprev->next = f->next;
  2501.                 else
  2502.                   continue;
  2503.             }
  2504.  
  2505.             while(GF_GETC(f, c))    /* pass input */
  2506.               GF_PUTC(f->next, c);
  2507.  
  2508.             GF_FLUSH(f->next);    /* and drain queue */
  2509.             fs_give((void **)&f->line);
  2510.             fs_give((void **)&f);    /* wax our data */
  2511.             return;
  2512.             }
  2513.             else
  2514.               continue;
  2515.         }
  2516.         else            /* add CR to buffer */
  2517.           GF_LINE_TEST_ADD(f, '\015');
  2518.         } /* fall thru to handle 'c' */
  2519.  
  2520.         if(c == '\015')        /* newline? */
  2521.           state = 1;
  2522.         else
  2523.           GF_LINE_TEST_ADD(f, c);
  2524.     }
  2525.  
  2526.     f->f1 = state;
  2527.     GF_END(f, f->next);
  2528.     }
  2529.     else if(flg == GF_EOD){
  2530.     GF_LINE_TEST_FLUSH(f);        /* BUG: should pass to test func? */
  2531.     fs_give((void **)&f->line);
  2532.     GF_FLUSH(f->next);
  2533.     (*f->next->f)(f->next, GF_EOD);
  2534.     }
  2535.     else if(flg == GF_RESET){
  2536.     dprint(9, (debugfile, "-- gf_reset line_test\n"));
  2537.     f->f1 = 0;            /* state */
  2538.     f->n  = 0L;            /* line number */
  2539.     f->f2 = LINE_TEST_BLOCK;    /* size of alloc'd line */
  2540.     f->line = p = (char *) fs_get(f->f2 * sizeof(char));
  2541.     f->data = (void *)gf_line_test_f;
  2542.     }
  2543.  
  2544.     f->linep = p;
  2545. }
  2546.  
  2547.  
  2548. /*
  2549.  * function called from the outside to operate on accumulated line.
  2550.  */
  2551. void
  2552. gf_line_test_opt(test_f)
  2553.     int  (*test_f) PROTO((long, char *));
  2554. {
  2555.     gf_line_test_f = test_f;
  2556. }
  2557.  
  2558.  
  2559. /*
  2560.  * TRAILING WHITESPACE FILTER - removes trailing whites space from each
  2561.  * line
  2562.  */
  2563.  
  2564. /*
  2565.  * Remove trailing space from each line.
  2566.  * As with all filters, assumes NVT end of lines.
  2567.  */
  2568. void
  2569. gf_delete_trail_space(f, flg)
  2570.     FILTER_S *f;
  2571.     int       flg;
  2572. {
  2573.     register char *p;
  2574.     GF_INIT(f, f->next);
  2575.  
  2576.     if(flg == GF_DATA){
  2577.     register unsigned char c;
  2578.     register int state = f->f1;
  2579.  
  2580.     while(GF_GETC(f, c)){
  2581.  
  2582.         switch(state){
  2583.           case CCR:
  2584.         state = DFL;
  2585.         if(c == '\012'){
  2586.             f->linep = f->line;    /* dump trailing space */
  2587.             GF_PUTC(f->next, '\015');
  2588.         }
  2589.         else{
  2590.             state = DFL;
  2591.             for(p = f->line; p < f->linep; p++)
  2592.               GF_PUTC(f->next, *p); /* output stored space */
  2593.  
  2594.             f->linep = f->line;
  2595.         }
  2596.         /* fall through to handle c */
  2597.  
  2598.           case DFL:
  2599.         if(c == ' ' || c == TAB){ /* store up white space */
  2600.             state = WSPACE;
  2601.             f->linep = f->line;
  2602.             *(f->linep)++ = c;
  2603.         }
  2604.         else
  2605.           GF_PUTC(f->next, c);
  2606.  
  2607.         break;
  2608.  
  2609.           case WSPACE :
  2610.         if(c == ' ' || c == TAB){ /* toss it in with other spaces */
  2611.             if(f->linep - f->line < GF_MAXLINE)
  2612.               *(f->linep)++ = c;
  2613.         }
  2614.         else if(c == '\015'){
  2615.             state = CCR;
  2616.         }
  2617.         else{
  2618.             state = DFL;
  2619.             for(p = f->line; p < f->linep; p++)
  2620.               GF_PUTC(f->next, *p); /* output stored space */
  2621.  
  2622.             f->linep = f->line;
  2623.             GF_PUTC(f->next, c); /* and the c */
  2624.         }
  2625.  
  2626.         break;
  2627.         }
  2628.     }
  2629.  
  2630.     f->f1 = state;
  2631.     GF_END(f, f->next);
  2632.     }
  2633.     else if(flg == GF_EOD){
  2634.     fs_give((void **)&(f->line));    /* free temp line buffer */
  2635.     GF_FLUSH(f->next);
  2636.     (*f->next->f)(f->next, GF_EOD);
  2637.     }
  2638.     else if(flg == GF_RESET){
  2639.     dprint(9, (debugfile, "-- gf_reset delete_trailing_space\n"));
  2640.     f->f1 = DFL;
  2641.     /* place to store white space */
  2642.     f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
  2643.     }
  2644. }
  2645.  
  2646.  
  2647. /*
  2648.  * Network virtual terminal to local newline convention filter
  2649.  */
  2650. void
  2651. gf_nvtnl_local(f, flg)
  2652.     FILTER_S *f;
  2653.     int       flg;
  2654. {
  2655.     GF_INIT(f, f->next);
  2656.  
  2657.     if(flg == GF_DATA){
  2658.     register unsigned char c;
  2659.     register int state = f->f1;
  2660.  
  2661.     while(GF_GETC(f, c)){
  2662.  
  2663. #ifdef    CRLF_NEWLINES
  2664.         /*
  2665.          * NOOP!
  2666.          */
  2667.         GF_PUTC(f->next, c);
  2668. #else
  2669.         if(state){
  2670.         state = 0;
  2671.         if(c == '\012'){
  2672.             GF_PUTC(f->next, '\012');
  2673.             continue;
  2674.         }
  2675.         else
  2676.           GF_PUTC(f->next, '\015');
  2677.         /* fall thru to deal with 'c' */
  2678.         }
  2679.  
  2680.         if(c == '\015')
  2681.           state = 1;
  2682.         else
  2683.           GF_PUTC(f->next, c);
  2684. #endif
  2685.     }
  2686.  
  2687.     f->f1 = state;
  2688.     GF_END(f, f->next);
  2689.     }
  2690.     else if(flg == GF_EOD){
  2691.     GF_FLUSH(f->next);
  2692.     (*f->next->f)(f->next, GF_EOD);
  2693.     }
  2694.     else if(flg == GF_RESET){
  2695.     dprint(9, (debugfile, "-- gf_reset nvtnl_local\n"));
  2696.     f->f1 = 0;
  2697.     }
  2698. }
  2699.  
  2700.  
  2701. /*
  2702.  * local to network newline convention filter
  2703.  */
  2704. void
  2705. gf_local_nvtnl(f, flg)
  2706.     FILTER_S *f;
  2707.     int       flg;
  2708. {
  2709.     GF_INIT(f, f->next);
  2710.  
  2711.     if(flg == GF_DATA){
  2712.     register unsigned char c;
  2713.  
  2714.     while(GF_GETC(f, c)){
  2715.  
  2716. #ifdef    CRLF_NEWLINES
  2717.         /*
  2718.          * NOOP!
  2719.          */
  2720.         GF_PUTC(f->next, c);
  2721. #else
  2722.         if(c == '\012'){
  2723.         GF_PUTC(f->next, '\015');
  2724.         GF_PUTC(f->next, '\012');
  2725.         }
  2726.         else
  2727.           GF_PUTC(f->next, c);
  2728. #endif
  2729.     }
  2730.  
  2731.     GF_END(f, f->next);
  2732.     }
  2733.     else if(flg == GF_EOD){
  2734.     GF_FLUSH(f->next);
  2735.     (*f->next->f)(f->next, GF_EOD);
  2736.     }
  2737.     else if(GF_RESET){
  2738.     dprint(9, (debugfile, "-- gf_reset local_nvtnl\n"));
  2739.     /* no op */
  2740.     }
  2741.  
  2742. }
  2743.  
  2744. #if defined(DOS) || defined(OS2)
  2745. /*
  2746.  * DOS CodePage to Character Set Translation (and back) filters
  2747.  */
  2748.  
  2749. /*
  2750.  * Charset and CodePage mapping table pointer and length
  2751.  */
  2752. static unsigned char *gf_xlate_tab;
  2753. static unsigned gf_xlate_tab_len;
  2754.  
  2755. /*
  2756.  * the simple filter takes DOS Code Page values and maps them into
  2757.  * the indicated external CharSet mapping or vice-versa.
  2758.  */
  2759. void
  2760. gf_translate(f, flg)
  2761.     FILTER_S *f;
  2762.     int       flg;
  2763. {
  2764.     GF_INIT(f, f->next);
  2765.  
  2766.     if(flg == GF_DATA){
  2767.     register unsigned char c;
  2768.  
  2769.     while(GF_GETC(f, c))
  2770.       if((unsigned) c < gf_xlate_tab_len)
  2771.         GF_PUTC(f->next, (int)gf_xlate_tab[c]);
  2772.  
  2773.     GF_END(f, f->next);
  2774.     }
  2775.     else if(flg == GF_EOD){
  2776.     GF_FLUSH(f->next);
  2777.     (*f->next->f)(f->next, GF_EOD);
  2778.     }
  2779.     else if(GF_RESET){
  2780.     dprint(9, (debugfile, "-- gf_reset translate\n"));
  2781.     /* no op */
  2782.     }
  2783. }
  2784.  
  2785.  
  2786. /*
  2787.  * function called from the outside to set
  2788.  * prefix filter's prefix string
  2789.  */
  2790. void
  2791. gf_translate_opt(xlatetab, xlatetablen)
  2792.     unsigned char *xlatetab;
  2793.     unsigned       xlatetablen;
  2794. {
  2795.     gf_xlate_tab     = xlatetab;
  2796.     gf_xlate_tab_len = xlatetablen;
  2797. }
  2798. #endif
  2799.  
  2800. /*
  2801.  * display something indicating we're chewing on something
  2802.  *
  2803.  * NOTE : IF ANY OTHER FILTERS WRITE THE DISPLAY, THIS WILL NEED FIXING
  2804.  */
  2805. void
  2806. gf_busy(f, flg)
  2807.     FILTER_S *f;
  2808.     int       flg;
  2809. {
  2810.     static short x = 0;
  2811.     GF_INIT(f, f->next);
  2812.  
  2813.     if(flg == GF_DATA){
  2814.     register unsigned char c;
  2815.  
  2816.     while(GF_GETC(f, c)){
  2817.  
  2818.         if(!((++(f->f1))&0x7ff)){     /* ding the bell every 2K chars */
  2819.         MoveCursor(0, 1);
  2820.         f->f1 = 0;
  2821.         if((++x)&0x04) x = 0;
  2822.         Writechar((x == 0) ? '/' :     /* CHEATING! */
  2823.               (x == 1) ? '-' : 
  2824.               (x == 2) ? '\\' : '|', 0);
  2825.         }
  2826.  
  2827.         GF_PUTC(f->next, c);
  2828.     }
  2829.  
  2830.     GF_END(f, f->next);
  2831.     }
  2832.     else if(flg == GF_EOD){
  2833.     MoveCursor(0, 1);
  2834.     Writechar(' ', 0);
  2835.     EndInverse();
  2836.     GF_FLUSH(f->next);
  2837.     (*f->next->f)(f->next, GF_EOD);
  2838.     }
  2839.     else if(flg == GF_RESET){
  2840.     dprint(9, (debugfile, "-- gf_reset busy\n"));
  2841.     f->f1 = 0;
  2842.         x = 0;
  2843.     StartInverse();
  2844.     }
  2845.  
  2846.     fflush(stdout);
  2847. }
  2848.